home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / PeerAware 1.03 / PeerAware-Setup.exe / Html / scripts / abstractrenderer.js next >
Text File  |  2008-01-06  |  37KB  |  1,129 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Cumulate Draw SVG Renderer.
  15.  *
  16.  * The Initial Developer of the Original Code is Cumulate Labs Inc.
  17.  * Portions created by Cumulate Labs Inc. are Copyright (C) 2006-2007
  18.  * Cumulate Labs Inc. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *
  22.  * Alternatively, the contents of this file may be used under the terms of
  23.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  24.  * in which case the provisions of the LGPL are applicable instead
  25.  * of those above. If you wish to allow use of your version of this file only
  26.  * under the terms of the LGPL, and not to allow others to
  27.  * use your version of this file under the terms of the MPL, indicate your
  28.  * decision by deleting the provisions above and replace them with the notice
  29.  * and other provisions required by the LGPL. If you do not delete
  30.  * the provisions above, a recipient may use your version of this file under
  31.  * the terms of any one of the MPL or the LGPL.
  32.  *
  33.  * ***** END LICENSE BLOCK ***** */
  34.  /**
  35.  *AbstractRenderer is the parent class for VMLRenderer and SVGRenderer. The intent is to provide
  36.  * a high level api for shape manipulation and hide the details in specific renderers. 
  37.  **/
  38.  
  39. /**
  40.  * constructor, do not invoke this directly, used by renderers to subclass.
  41.  */
  42.  
  43. function AbstractRenderer() {
  44.  
  45. };
  46.  
  47. AbstractRenderer.prototype.init = function(elem) {
  48.     this.VML_SVG_NAMESPACE="VML_SVG";
  49. };
  50. ///////////////////////////////////////BEGIN OVERRIDDEN METHODS///////////////////////////////////////
  51. /**
  52.  * return the bounds of the shape
  53.  * @see VMLRenderer.bounds
  54.  * @see SVGRenderer.bounds
  55.  */
  56. AbstractRenderer.prototype.bounds = function(shape) { return { x:0, y:0, width:0, height: 0 }; };
  57. /**
  58.  * initial call to create a shape (but not connector, for connectors see createLine)
  59.  */
  60. AbstractRenderer.prototype.create = function(shape, fillColor, lineColor, lineWidth, left, top, width, height,opacity,gradient) {};
  61. /**
  62.  * remove a node
  63.  */
  64. AbstractRenderer.prototype.remove = function(shape) {};
  65. /**
  66.  * move a shape to new left and to location
  67.  */
  68. AbstractRenderer.prototype.move = function(shape, left, top) {};
  69. AbstractRenderer.prototype.fineMove = function(shape, move,isHorizontal) {};
  70. /**
  71.  * get the underlying shpae which cause this event
  72.  */
  73. AbstractRenderer.prototype.getShapeFromEventSource=function(source){};
  74. /**
  75. *get from x for a valid connector
  76. **/
  77. AbstractRenderer.prototype.getConnectorFromX=function(shape){}
  78. /**
  79. *get from y for a valid connector
  80. **/
  81. AbstractRenderer.prototype.getConnectorFromY=function(shape){}
  82. /**
  83. *get to x for a valid connector
  84. **/
  85. AbstractRenderer.prototype.getConnectorToX=function(shape){}
  86. /**
  87. *get to y for a valid connector
  88. **/
  89.  
  90. AbstractRenderer.prototype.getConnectorToY=function(shape){}
  91.  
  92. /**
  93. *set shape width
  94. **/
  95. AbstractRenderer.prototype.setWidth=function(shape,width){}
  96. /**
  97. *set shape height
  98. **/
  99. AbstractRenderer.prototype.setHeight=function(shape,height){}
  100. /**
  101. *set shape x (left)
  102. **/
  103. AbstractRenderer.prototype.setX=function(shape,left){}
  104. /**
  105. *set shape Y(top)
  106. **/
  107. AbstractRenderer.prototype.setY=function(shape,top){
  108. }
  109.  
  110. /**
  111. *get all shape objects (not including connectors)!
  112. **/
  113. AbstractRenderer.prototype.getAllShapes=function(doc){}
  114. /**
  115. *get all connectors (but not shapes)
  116. **/
  117. AbstractRenderer.prototype.getAllConnectors=function(doc){
  118. }
  119. /**
  120. *Only used during draw, for general resize using handles see resizeWidth, resizeHeight
  121. **/
  122. AbstractRenderer.prototype.resize = function(shape, fromX, fromY, toX, toY) {};
  123. /**
  124.  * @see vmlrenderer#editCommand
  125.  * @see svgrenderer#editCommand
  126.  */
  127. AbstractRenderer.prototype.editCommand = function(shape, cmd, value) {};
  128. /**
  129.  * @see vmlrenderer#queryCommand
  130.  * @see svgrenderer#queryCommand
  131.  * 
  132.  */
  133. AbstractRenderer.prototype.queryCommand = function(shape, cmd) {};
  134. AbstractRenderer.prototype.showTracker = function(shape) {};
  135. AbstractRenderer.prototype.updateTracker = function(shape) {};
  136. AbstractRenderer.prototype.getMarkup = function() { return null; };
  137. AbstractRenderer.prototype.fineRotateSelection=function(shape,angle){};
  138. AbstractRenderer.prototype.rotate=function(shape,angle){};
  139. AbstractRenderer.prototype.setCursor=function(shape,cursor){};
  140. AbstractRenderer.prototype.setFillColor=function(shape,fillColor){};
  141. AbstractRenderer.prototype.setShapeText=function(shape,text,font,override,zoom){};
  142. AbstractRenderer.prototype.getShapeText=function(shape){};
  143. AbstractRenderer.prototype.clearShapeText=function(shape){};
  144. AbstractRenderer.prototype.sendToBack=function(shape){};
  145. AbstractRenderer.prototype.bringToFront=function(shape){};
  146. AbstractRenderer.prototype.updateZIndex=function(shape){};
  147. AbstractRenderer.prototype.setLineStyle=function(connector,style){};
  148. AbstractRenderer.prototype.getLineStyle=function(connector){};
  149. AbstractRenderer.prototype.setStrokeWidth=function(shape,value){};
  150. AbstractRenderer.prototype.getStrokeWidth=function(shape){};
  151.  
  152. AbstractRenderer.prototype.isConnector=function(shape){};
  153. //line draw functions
  154. AbstractRenderer.prototype.createLine=function(mode,lineColor,lineWidth,left,top,width,height){};
  155. AbstractRenderer.prototype.moveLine=function(line,toX,toY,isFrom){};//isfrom indicates which end is being moved
  156. AbstractRenderer.prototype.moveLinePoint=function(line,toX,toY,isFrom){};//isfrom indicates which end is being moved
  157. AbstractRenderer.prototype.moveLineWithShape=function(shape){};
  158. AbstractRenderer.prototype.showConnectionPoints=function(shape){};
  159. AbstractRenderer.prototype.connectLine=function(shape,line,toOrFrom){};
  160. /**
  161. * this method is implemented only in the vml renderer to fix the polyline points issue in IE
  162. **/
  163. AbstractRenderer.prototype.savePolyLinePaths=function(){};
  164.  
  165. AbstractRenderer.prototype.appendPageAttribute=function(div,att){};
  166. AbstractRenderer.prototype.getRealData=function(){};
  167. AbstractRenderer.prototype.updateRotation=function(shape){}
  168. /**
  169.  * return all the subshapes for a given shape
  170.  * @param shape
  171.  * @return list of all subshapes
  172.  */
  173. AbstractRenderer.prototype.getAllSubShapes=function(shape){};
  174. /**
  175. *this method will return the actual subject of a shape, the subject could either be a path or line,polyline in case
  176. of connectors, this assumes that the shape has only one subject, if a shape is comples with multiple sub-shapes
  177. * see getAllSubshapes.
  178. **/
  179. AbstractRenderer.prototype.getShapeSubject=function(shape){};
  180.  
  181. ////////////////////////////////////END OVERRIDDEN FUNCTIONS//////////////////////////////////////////
  182.  
  183. /**
  184. *resize shape width, this is a hacked method to compensate for shape movement during rotated resize. Ideally 
  185. * we should do some combination of translate rotate and translate back
  186. * @param theshape
  187. * @param current mouse X
  188. * @param current mouse y
  189. * @param initial mouse down x
  190. * @param initia mouse down x
  191. * @param prev width (before resize started)
  192. * @param is right or left stretch
  193. *
  194. **/
  195.  
  196. AbstractRenderer.prototype.resizeWidth = function(shape,snappedX,snappedY, mouseDownX,mouseDownY,prevWidth,isRightStretch) {
  197.     var distanceA=((snappedX-mouseDownX)*(snappedX-mouseDownX))+((snappedY-mouseDownY)*(snappedY-mouseDownY));
  198.     distanceA=Math.sqrt(distanceA);
  199.     var bounds=this.bounds(shape);
  200.     var rotation=bounds.rotation;
  201.     if(!rotation||rotation=="undefined"){
  202.         rotation=0;
  203.     }
  204.     rotation =rotation%360;
  205.     var snpX=(snappedX*Math.cos((Math.PI/180)*(-rotation)))-(snappedY*Math.sin((Math.PI/180)*(-rotation)));
  206.     var mouseX=(mouseDownX*Math.cos((Math.PI/180)*(-rotation)))-(mouseDownY*Math.sin((Math.PI/180)*(-rotation)));
  207.       if(snpX<mouseX&&(isRightStretch)){
  208.           distanceA=-distanceA;
  209.       }
  210.       else if(snpX>mouseX&&(!isRightStretch)){
  211.           distanceA=-distanceA;
  212.       }
  213.       
  214.       var width=prevWidth;
  215.       
  216.       width=(width*1)+distanceA;
  217.     if(width<1){
  218.         return false;
  219.     }
  220.     
  221.       var obj=null;
  222.       if(isRightStretch)
  223.           obj=this.getRotatedPoint(bounds.x,bounds.y,bounds);
  224.       else
  225.           obj=this.getRotatedPoint((bounds.x*1)+(bounds.width*1),(bounds.y*1)+(bounds.height*1),bounds);
  226.  
  227.     this.setWidth(shape,width);
  228.       var newBounds=this.bounds(shape);
  229.       var newObj=null;
  230.       if(isRightStretch)
  231.           newObj=this.getRotatedPoint(newBounds.x,newBounds.y,newBounds);
  232.       else
  233.           newObj=this.getRotatedPoint((newBounds.x*1)+(newBounds.width*1),(newBounds.y*1)+(newBounds.height*1),newBounds);
  234.       var xFactor=Math.round(obj.x)-Math.round(newObj.x);
  235.       var yFactor=Math.round(obj.y)-Math.round(newObj.y);
  236.       
  237.       this.setX(shape,(newBounds.x*1)+xFactor);
  238.     this.setY(shape,(newBounds.y*1)+yFactor);
  239.     this.updateRotation(shape);
  240.   
  241. };
  242.  
  243.  
  244. /**
  245. *resize height
  246. *  @param theshape
  247. * @param current mouse X
  248. * @param current mouse y
  249. * @param initial mouse down x
  250. * @param initia mouse down x
  251. * @param prev height (before resize started)
  252. * @param is top or bottom stretch
  253. **/
  254. AbstractRenderer.prototype.resizeHeight = function(shape, snappedX,snappedY, mouseDownX,mouseDownY,prevHeight,isBottomStretch) {
  255.  var distanceA=((snappedY-mouseDownY)*(snappedY-mouseDownY))+((snappedX-mouseDownX)*(snappedX-mouseDownX));
  256.     distanceA=Math.sqrt(distanceA);
  257.     var bounds=this.bounds(shape);
  258.     var rotation=bounds.rotation;
  259.     if(!rotation||rotation=="undefined"){
  260.         rotation=0;
  261.     }
  262.     rotation =rotation%360;
  263.     var snpY=(snappedY*Math.cos((Math.PI/180)*(-rotation)))+(snappedX*Math.sin((Math.PI/180)*(-rotation)));
  264.     var mouseY=(mouseDownY*Math.cos((Math.PI/180)*(-rotation)))+(mouseDownX*Math.sin((Math.PI/180)*(-rotation)));
  265.       if(snpY<mouseY&&isBottomStretch){
  266.           distanceA=-distanceA;
  267.       }
  268.       else if(snpY>mouseY&&(!isBottomStretch)){
  269.           distanceA=-distanceA;
  270.       }
  271.       var height=prevHeight;
  272.       height=(height*1)+distanceA;
  273.     if(height<1){
  274.         return false;
  275.     }
  276.     var obj=null;
  277.       if(isBottomStretch)
  278.           obj=this.getRotatedPoint(bounds.x,bounds.y,bounds);
  279.       else
  280.           obj=this.getRotatedPoint((bounds.x*1)+(bounds.width*1),(bounds.y*1)+(bounds.height*1),bounds);
  281.  
  282.     this.setHeight(shape,height);
  283.       var newBounds=this.bounds(shape);
  284.       var newObj=null;
  285.       if(isBottomStretch)
  286.           newObj=this.getRotatedPoint(newBounds.x,newBounds.y,newBounds);
  287.       else
  288.           newObj=this.getRotatedPoint((newBounds.x*1)+(newBounds.width*1),(newBounds.y*1)+(newBounds.height*1),newBounds);
  289.       var xFactor=Math.round(obj.x)-Math.round(newObj.x);
  290.       var yFactor=Math.round(obj.y)-Math.round(newObj.y);
  291.       //alert("xFactor:"+xFactor+"yFactor:"+yFactor);
  292.       this.setX(shape,(newBounds.x*1)+xFactor);
  293.     this.setY(shape,(newBounds.y*1)+yFactor);
  294.     this.updateRotation(shape);
  295.  
  296.  
  297.   
  298. };
  299.  
  300. /**
  301.  * move the complete line segment, used for drag. Moves both ends of the line/polyline/curve and control points
  302.  * if any
  303.  * @param shape
  304.  * @param original shape bounds
  305.  * @param {int} deltaX
  306.  * @param {int} deltaY
  307.  */
  308. AbstractRenderer.prototype.moveCompleteLine=function(shape,bounds,deltaX,deltaY){
  309.     if(!this.isConnector(shape)){
  310.         return;
  311.     }
  312.     var type=bounds.type;
  313.     var fromX=(bounds.x*1)+deltaX;
  314.     var fromY=(bounds.y*1)+deltaY;
  315.     var toX=(bounds.x2*1)+deltaX;
  316.     var toY=(bounds.y2*1)+deltaY;
  317.     this.moveLine(shape,fromX,fromY,true);
  318.     this.moveLine(shape,toX,toY,false);
  319.     if(type=='curve-line'){
  320.         var controlX=(bounds.controlX*1)+deltaX;
  321.         var controlY=(bounds.controlY*1)+deltaY;
  322.         var controlX2=(bounds.controlX2*1)+deltaX;
  323.         var controlY2=(bounds.controlY2*1)+deltaY;
  324.         this.setControl1(shape,controlX,controlY);
  325.         this.setControl2(shape,controlX2,controlY2);
  326.     }
  327.     
  328. }
  329.  
  330. /**
  331.  * get the center point location for a connector, for line and ortholine: 
  332.  * centerpoint=(x2+x1/2),(y2+y1/2)
  333.  * For curved connectors, the center point is (centerControlPoints+centerEndPoints)/2
  334.  * @return {object} with attributes x and y
  335.  */
  336.  AbstractRenderer.prototype.getConnectorCenterPoint=function(shape){
  337.      var bounds=this.bounds(shape);
  338.      var type=this.getConnectorType(shape);
  339.      var obj=new Object();
  340.      if(type=='line'){
  341.          obj.x=((bounds.x*1)+(bounds.x2*1))/2;
  342.          obj.y=((bounds.y*1)+(bounds.y2*1))/2;
  343.      }
  344.      else if (type=='curve-line'){
  345.          var controlCenterX=((bounds.controlX*1)+(bounds.controlX2*1))/2;
  346.          var controlCenterY=((bounds.controlY*1)+(bounds.controlY2*1))/2;
  347.          var pointsCenterX=((bounds.x*1)+(bounds.x2*1))/2;
  348.          var pointsCenterY=((bounds.y*1)+(bounds.y2*1))/2;
  349.          obj.x=((controlCenterX*1)+(pointsCenterX))/2;
  350.          obj.y=((controlCenterY*1)+(pointsCenterY))/2;
  351.      }
  352.      else if (type=='ortho-line'){
  353.          var bounds=this.getOrthoLineCenterSegment(shape);
  354.          obj.x=((bounds.x*1)+(bounds.x2*1))/2;
  355.          obj.y=((bounds.y*1)+(bounds.y2*1))/2;
  356.      }
  357.      return obj;
  358.  }
  359. /**
  360.  * TODO: should be renamed to getTextSize since it returns the height and width of any text, not just
  361.  * connector text
  362.  * @param theshape
  363.  * @param thetext
  364.  * @param current zoomfactor
  365.  * @param current font (if any)
  366.  */
  367. AbstractRenderer.prototype.getConnectorTextSize=function(shape,editText,zoom,oldFont){
  368.     
  369.     var text=null;
  370.     var font=oldFont;
  371.     if(editText)
  372.         text=editText;
  373.     else
  374.         text=this.getShapeText(shape);
  375.     if(font==null){
  376.         font=this.getFont(shape);
  377.     }
  378.     //if no font, use default
  379.     if(font.size.length==0){
  380.         this.fillUpFont(font);
  381.         font.size=font.size*zoom;
  382.     }
  383.     
  384.     
  385.     
  386.     //first calculate the number of lines
  387.     var numLines=1;
  388.     var last = 0;
  389.     while ( true ) {
  390.         last = text.indexOf("\n", last+1);
  391.        if ( last == -1 ) break;
  392.        numLines ++;
  393.     }
  394.    // if(numLines>1)numLines--;
  395.     //height will be numlines*2*font
  396.     var height=numLines*font.size*1.5;
  397.     //width will be the longest line +20
  398.     var lines=text.split("\n");
  399.     var highWidth=50;
  400.     for (var i=0;i<lines.length;i++){
  401.         if(lines[i].length*font.size/2>highWidth)
  402.             highWidth=lines[i].length*font.size/2;
  403.     }
  404.     highWidth=(highWidth*1)+10;
  405.     var obj=new Object();
  406.     obj.width=highWidth;
  407.     obj.height=height;
  408.     return obj;
  409. }
  410.  
  411. AbstractRenderer.prototype.calculateOrthoLinePath=function(shape,fromX,fromY,toX,toY){
  412.     var points1=this.getClearancePoints(shape,fromX,fromY,toX,toY,true);
  413.     var points2=this.getClearancePoints(shape,toX,toY,points1[points1.length-1].x,points1[points1.length-1].y,false);
  414.     var x1=points1[points1.length-1].x;
  415.     var y1=points1[points1.length-1].y;
  416.     var x2=points2[points2.length-1].x;
  417.     var y2=points2[points2.length-1].y;
  418.     var obj=new Object();
  419.  
  420.     obj.x=x1;
  421.     obj.y=y2;
  422.     
  423.     points1[points1.length]=obj;
  424.     for(var i=points2.length-1;i>=0;i--){
  425.         points1[points1.length]=points2[i];
  426.     
  427.     }
  428.     var path="";
  429.     for (var i=0;i<points1.length;i++){
  430.         path+=points1[i].x+",";
  431.         path+=points1[i].y+",";
  432.     
  433.     }
  434.     return path;
  435.  
  436.  
  437.  
  438. }
  439. /**
  440. *get the clearance points for a given shape
  441. **/
  442. AbstractRenderer.prototype.getClearancePoints=function(line,fromX,fromY,toX,toY,isFrom){
  443.         //debugger;
  444.         var TOP=0;
  445.         var RIGHT=1;
  446.         var BOTTOM=2;
  447.         var LEFT=3;
  448.         var CONSTANT=12;
  449.         var array=new Array();
  450.         var obj=new Object();
  451.         obj.x=fromX;
  452.         obj.y=fromY;
  453.         array[array.length]=obj;
  454.         var shapeProps=this.getConnectionShape(line,isFrom);
  455.         if(!$(shapeProps.shapeid)){
  456.             
  457.             return array;
  458.         }
  459.         
  460.         var shape=$(shapeProps.shapeid);
  461.         var bounds=this.bounds(shape);
  462.         var loc=this.getConnectionPointLocation(shape,shapeProps.shapepoint,bounds);//implement
  463.         var clear=this.getRotatedClearancePoints(bounds,loc,CONSTANT);//implement,clear[0] will be first clearance, clear [1] will be second clearance
  464.         var orient=(parseInt((bounds.rotation)/90)+(loc*1))%4;
  465.         //add basic clearance
  466.         obj=new Object();
  467.         if(orient==TOP){
  468.             var factor=Math.abs(fromY-toY)/2;
  469.             if(factor<CONSTANT)factor=CONSTANT;
  470.             obj.y=array[array.length-1].y-CONSTANT;//replace this with constant!!!
  471.             obj.x=array[array.length-1].x;
  472.         }
  473.         else if(orient==BOTTOM){
  474.             var factor=Math.abs(fromY-toY)/2;
  475.             if(factor<CONSTANT)factor=CONSTANT;
  476.             obj.y=array[array.length-1].y+CONSTANT;//replace this with constant!!!
  477.             obj.x=array[array.length-1].x;
  478.         }
  479.         else if(orient==RIGHT){
  480.             var factor=Math.abs(fromX-toX)/2;
  481.             if(factor<CONSTANT)factor=CONSTANT;
  482.             obj.x=array[array.length-1].x+CONSTANT;//replace this with constant!!!
  483.             obj.y=array[array.length-1].y;
  484.         }
  485.         else if(orient==LEFT){
  486.             var factor=Math.abs(fromX-toX)/2;
  487.             if(factor<CONSTANT)factor=CONSTANT;
  488.             obj.x=array[array.length-1].x-CONSTANT;//replace this with constant!!!
  489.             obj.y=array[array.length-1].y;
  490.         }
  491.         array[array.length]=obj;
  492.         //now check for additional clearance
  493.         if(orient==TOP&&obj.y<toY){//target location is below, we need additional clearance
  494.             var nextObj=new Object();
  495.             if(toX<fromX){
  496.                 nextObj.x=toX>((clear[0].x*1)-CONSTANT)?toX:((clear[0].x*1)-CONSTANT);
  497.                 
  498.             }
  499.             else{
  500.                 
  501.                 nextObj.x=toX<((clear[1].x*1)+(CONSTANT*1))?toX:((clear[1].x*1)+(CONSTANT*1));
  502.                 
  503.             }
  504.             nextObj.y=obj.y;//fix later
  505.             array[array.length]=nextObj;
  506.         }
  507.         else if(orient==BOTTOM&&obj.y>toY){//target location is, we need additional clearance
  508.             var nextObj=new Object();
  509.             if(toX<fromX){
  510.                 
  511.                 nextObj.x=toX>((clear[1].x*1)-(CONSTANT*1))?toX:((clear[1].x*1)-(CONSTANT*1));
  512.                 
  513.             }
  514.             else{
  515.                 nextObj.x=toX<((clear[0].x*1)+(CONSTANT*1))?toX:((clear[0].x*1)+(CONSTANT*1));
  516.                 
  517.             }
  518.             nextObj.y=obj.y;//fix later
  519.             array[array.length]=nextObj;
  520.         }
  521.         else if(orient==RIGHT&&obj.x>toX){//target location is left, we need additional clearance
  522.             var nextObj=new Object();
  523.             if(toY<fromY){
  524.                 nextObj.y=toY>((clear[0].y*1)-CONSTANT)?toY:((clear[0].y*1)-CONSTANT);
  525.                 
  526.             }
  527.             else{
  528.                 nextObj.y=toY<((clear[1].y*1)+(CONSTANT*1))?toY:((clear[1].y*1)+(CONSTANT*1));
  529.             }
  530.             nextObj.x=obj.x;//fix later
  531.             array[array.length]=nextObj;
  532.         }
  533.         else if(orient==LEFT&&obj.x<toX){//target location is right, we need additional clearance
  534.             var nextObj=new Object();
  535.             
  536.             if(toY<fromY){
  537.                 
  538.                 nextObj.y=toY>((clear[1].y*1)-CONSTANT)?toY:((clear[1].y*1)-CONSTANT);
  539.                 
  540.             }
  541.             else{
  542.                 
  543.                 nextObj.y=toY<((clear[0].y*1)+(CONSTANT*1))?toY:((clear[0].y*1)+(CONSTANT*1));
  544.             }
  545.             nextObj.x=obj.x;//fix later
  546.             array[array.length]=nextObj;
  547.         }
  548.     return array;
  549. }
  550. /**
  551. * get the location of the point
  552. *@param shape
  553. *@param pointindex
  554. *@param bounds
  555. *@return orientation: 0:top,1:right,2:bottom,3:left
  556. **/
  557. AbstractRenderer.prototype.getConnectionPointLocation=function(shape,point,bounds){
  558.     var TOP=0;
  559.     var RIGHT=1;
  560.     var BOTTOM=2;
  561.     var LEFT=3;
  562.     var pointNode=shape.getElementsByTagName("connection-point")[point];
  563.     var x=this.getAttribute(pointNode,"x");
  564.     var y=this.getAttribute(pointNode,"y");
  565.     x=(bounds.x*1)+(bounds.width*(x/this.COORD_X));
  566.     y=(bounds.y*1)+(bounds.height*(y/this.COORD_Y));
  567.     if(x<(bounds.x*1+bounds.width/2))return LEFT;
  568.     else if(x>(bounds.x*1)+(bounds.width/2))return RIGHT;
  569.     else if(y<=bounds.y)return TOP;
  570.     else  return BOTTOM;
  571.  
  572.  
  573. }
  574. /**
  575. *return the rotated locaction of the shape clearances, clearance will vary by location
  576. *@param {array}shape bounds
  577. *@param {int}location 0:top,1:right,2:bottom,3:left
  578. *@param {int}CONSTANT, how much to add to the clearance
  579. **/
  580. AbstractRenderer.prototype.getRotatedClearancePoints=function(bounds,loc,CONSTANT){
  581.     var TOP=0;
  582.     var RIGHT=1;
  583.     var BOTTOM=2;
  584.     var LEFT=3;
  585.     var array=new Array();
  586.     array[array.length]=new Object();
  587.     array[array.length]=new Object();
  588.     if(loc==TOP){
  589.          array[0].x=bounds.x;
  590.          array[0].y=bounds.y;
  591.          array[1].x=(bounds.x*1)+(bounds.width*1);
  592.          array[1].y=bounds.y;
  593.         
  594.     }
  595.     else if(loc==RIGHT){
  596.          array[0].x=(bounds.x*1)+(bounds.width*1);
  597.          array[0].y=bounds.y;
  598.          array[1].x=(bounds.x*1)+(bounds.width*1);
  599.          array[1].y=(bounds.y*1)+(bounds.height*1);
  600.          
  601.     }
  602.     else if(loc==BOTTOM){
  603.         array[0].x=(bounds.x*1)+(bounds.width*1);
  604.         array[0].y=(bounds.y*1)+(bounds.height*1);
  605.         array[1].x=(bounds.x*1);
  606.         array[1].y=(bounds.y*1)+(bounds.height*1);
  607.         
  608.         
  609.     }
  610.     else{
  611.         array[0].x=(bounds.x*1);
  612.         array[0].y=(bounds.y*1)+(bounds.height*1);
  613.         array[1].x=(bounds.x*1);
  614.         array[1].y=(bounds.y*1);
  615.         
  616.     
  617.     }
  618.     return array;//todo rotate these...
  619. }
  620. /**
  621. *check if a line is connected,return  the shape id and point
  622. * @param theline(line/polyline/curve)
  623. * @param isFrom --are we checking from or to end of the line
  624. * @return {object} contains shapeid and the point number this line is connected to
  625. **/
  626. AbstractRenderer.prototype.getConnectionShape=function(line,isFrom){
  627.     var nodes=line.getElementsByTagName("connection");
  628.     var shapeid="";
  629.     var shapepoint="";
  630.     for(var i=0;i<nodes.length;i++){
  631.         var type=this.getAttribute(nodes[i],"type");
  632.         if(isFrom&&type=='from'){
  633.             shapeid=this.getAttribute(nodes[i],"shapeid");
  634.             shapepoint=this.getAttribute(nodes[i],"shapepoint");
  635.         }
  636.         else if(!isFrom&&type=='to'){
  637.             shapeid=this.getAttribute(nodes[i],"shapeid");
  638.             shapepoint=this.getAttribute(nodes[i],"shapepoint");
  639.         }
  640.     
  641.     }
  642.     var obj=new Object();
  643.     obj.shapeid=shapeid;
  644.     obj.shapepoint=shapepoint;
  645.     return obj;
  646. }
  647.  
  648. /**
  649. *we assume that the shape has already moved,find the connectors attached to a shape and move them to match
  650. * the current location of the shape connection point they are connected to.
  651. * @param shape
  652. **/
  653. AbstractRenderer.prototype.moveLineWithShape=function(shape){
  654.     //debugger;
  655.     //first get all connections for this shape
  656.     var rect=this.bounds(shape);
  657.     var left=rect['x'];
  658.     var top=rect['y'];
  659.     var width=rect['width'];
  660.     var height=rect['height'];
  661.     var centerX=parseInt(left*1)+parseInt(width/2);
  662.     var centerY=parseInt(top*1)+parseInt(height/2);
  663.     //get the rotation
  664.     var rotation=rect['rotation'];
  665.     if(!rotation)rotation=0;
  666.     //get the shape coordinate size, hardcode for now
  667.     coordX=1000;
  668.     coordY=1000;
  669.     var connectList=shape.getElementsByTagName("connection");
  670.     for(var i=0;i<connectList.length;i++){
  671.         var connection=connectList.item(i);
  672.         var point=connection.parentNode;
  673.         var lineId=connection.getAttribute("lineid");
  674.         var toOrFrom=connection.getAttribute("type");
  675.         //for each connection point get its(unrotated) pixel location
  676.         var conX=(left*1)+((point.getAttribute("x")/this.COORD_X)*width);
  677.         var conY=(top*1)+((point.getAttribute("y")/this.COORD_Y)*height);
  678.         //now translate the center to the origin
  679.         conX=conX-centerX;
  680.         conY=conY-centerY;
  681.         //now rotate and translate back
  682.         var finalX=((conX*Math.cos((Math.PI/180)*(rotation)))-(conY*Math.sin((Math.PI/180)*(rotation)))*1)+(centerX*1);
  683.         var finalY=((conX*Math.sin((Math.PI/180)*(rotation)))+(conY*Math.cos((Math.PI/180)*(rotation)))*1)+(centerY*1);
  684.         if(toOrFrom=="to")
  685.             this.moveLine($(lineId),finalX,finalY,false);
  686.         else
  687.             this.moveLine($(lineId),finalX,finalY,true);
  688.     }
  689.  
  690. }
  691. /*
  692.  * return an array of all the connectors attached to the shape
  693.  * @param shape
  694.  * @return array of connectors
  695.  */
  696. AbstractRenderer.prototype.getAllConnectorsForShape=function(shape){
  697.     if(!shape)return;
  698.     var connectList=shape.getElementsByTagName("connection");
  699.     var array=new Array();
  700.     for(var i=0;i<connectList.length;i++){
  701.         var connection=connectList.item(i);
  702.         var point=connection.parentNode;
  703.         var lineId=connection.getAttribute("lineid");
  704.         if($(lineId))array[array.length]=$(lineId);    
  705.     }
  706.     return array;
  707. }
  708.  
  709. /**
  710. *add the connection attribute to the shape and the line
  711. **/
  712. AbstractRenderer.prototype.connectLineToShape=function(shape,line,toOrFrom,connectionPoint,pointIndex){
  713.     if(!shape||!line||(pointIndex<0))return;
  714.     var nodeList=line.getElementsByTagName("connection");
  715.     for(var i=0;i<nodeList.length;i++){
  716.         var node=nodeList.item(i);
  717.         var type=node.getAttribute("type");
  718.         if(type==toOrFrom){
  719.             node.setAttribute("shapeid",shape.id);
  720.             node.setAttribute("shapepoint",pointIndex);
  721.             var doc=this.container.ownerDocument;
  722.             var connection=this.createElement("c:connection","CUMULATE_LABS");
  723.             connection.setAttribute("lineid",line.id);
  724.             connection.setAttribute("type",toOrFrom);
  725.             connectionPoint.appendChild(connection);    
  726.         }
  727.         
  728.     }
  729.  
  730. }
  731. /**
  732. *disconnect the line i.e null out connection attributes in both the line and shape
  733. **/
  734. AbstractRenderer.prototype.disconnectLineFromShape=function(line,toOrFrom){
  735.     //debugger;
  736.     if(!line)return;
  737.     var nodeList=line.getElementsByTagName("connection");
  738.     for(var i=0;i<nodeList.length;i++){
  739.         var node=nodeList.item(i);
  740.         var type=node.getAttribute("type");
  741.         if(type==toOrFrom){
  742.             var shapeId=node.getAttribute("shapeid");
  743.             var point=node.getAttribute("shapepoint");
  744.             if(shapeId&&shapeId.length>0){
  745.                 var shape=$(shapeId);
  746.                 if(shape){
  747.                     setHelp("Disconnected line from shape");
  748.                     var connectionPoint=shape.getElementsByTagName("connection-point").item(point*1);
  749.                     var connectList=connectionPoint.getElementsByTagName("connection");
  750.                     for(var i=0;i<connectList.length;i++){
  751.                         var connect=connectList.item(i);
  752.                         if((connect.getAttribute("lineid")==line.id)&&(connect.getAttribute("type")==type)){
  753.                             this.remove(connect);
  754.                             break;
  755.                         }
  756.                     }
  757.                 }
  758.                 node.setAttribute("shapeid","xx");
  759.                 node.setAttribute("shapepoint","-1");    
  760.             }
  761.             
  762.         }
  763.     }
  764.  
  765. }
  766.  
  767. /**
  768.  * given the shape bounds return an object with the centerx and y
  769.  * @return {object} with 2 attributes: centerX, centerY
  770.  */
  771. AbstractRenderer.prototype.getCenterPoint=function(bounds){
  772.     if(bounds){
  773.         var left=bounds.x;
  774.         var top=bounds.y;
  775.         var width=bounds.width;
  776.         var height=bounds.height;
  777.         var obj=new Object();
  778.         obj.centerX=(left*1)+(width/2);
  779.         obj.centerY=(top*1)+(height/2);
  780.         return obj;
  781.     }
  782.     
  783. }
  784. /**
  785.  * get rotated point location. Given a point get its rotated location.
  786.  * @param {number}conX
  787.  * @param {number}conY
  788.  * @param bounds array with (x,y,width,height,rotation)
  789.  */
  790.  AbstractRenderer.prototype.getRotatedPoint=function(conX,conY,bounds){
  791.      var rotation=bounds.rotation;
  792.      var center=this.getCenterPoint(bounds);
  793.      var centerX=center.centerX;
  794.      var centerY=center.centerY;
  795.      //now translate the center to the origin
  796.     conX=conX-centerX;
  797.     conY=conY-centerY;
  798.     //now rotate and translate back
  799.     var obj=new Object();
  800.      obj.x=((conX*Math.cos((Math.PI/180)*(rotation)))-(conY*Math.sin((Math.PI/180)*(rotation)))*1)+(centerX*1);
  801.     obj.y=((conX*Math.sin((Math.PI/180)*(rotation)))+(conY*Math.cos((Math.PI/180)*(rotation)))*1)+(centerY*1);
  802.     return obj;
  803.  }
  804.  
  805. /**
  806. *To set the text of the shape, we have to do the following
  807. *1. Get the text box bounds of the selected shape:
  808. *2. Create a text set i.e break each line by new line token(<br/>) except for the first every text set is of type "newline"
  809. *3. Calculate the distance between each line, see formula in handleTextPositioning
  810. *5. Create line shapes for each text set using chars/line i.e divide each text set into line shapes and add to list,for the first
  811. * line of each textset except new line, the type is "newline"
  812. *6. Now start from the center of the shape list and draw everything above it and below it 
  813. **/
  814.  
  815. AbstractRenderer.prototype.setShapeText=function(shape,text,font,isSizeOverride,oldFont,zoomFactor/**zoom is only used for font**/){
  816.     
  817.     var shapeFont=oldFont;
  818.     if(!shapeFont)
  819.         shapeFont=this.getFont(shape);
  820.     //now lets fill in whats not available
  821.     if(shapeFont.size==''||isSizeOverride){
  822.         shapeFont.size=font.size;
  823.         
  824.     }
  825.     if(shapeFont.color==''){
  826.         shapeFont.color=font.color;
  827.     }
  828.     if(shapeFont.family==''){
  829.         shapeFont.family=font.family;
  830.     }
  831.     if(shapeFont.align==''){
  832.         shapeFont.align=font.align;
  833.     }
  834.     if(shapeFont.bold==''){
  835.         shapeFont.bold=font.bold;
  836.     }
  837.     if(shapeFont.italics==''){
  838.         shapeFont.italics=font.italics;
  839.     }
  840.     
  841.     /**we need to preserve this so that whenever the shape is moved,it is used**/
  842.     var actualShapeFontSize=shapeFont.size;
  843.     var fontSize=shapeFont.size*zoomFactor;
  844.     shapeFont.size=fontSize;
  845.     
  846.     var rect=this.getTextBounds(shape,text,zoomFactor,shapeFont);
  847.     
  848.     var width=rect["width"];
  849.     var height=rect["height"];
  850.     var x=rect['x'];
  851.     var y=rect['y'];
  852.     if(width<25){
  853.         width=25;
  854.     }
  855.     if(height==0){
  856.         height=1;
  857.     }
  858.     //chars per line
  859.     var cpl=((width/fontSize))*2;
  860.     cpl=Math.ceil(cpl);
  861.     //wrap the text, false indicates--break the line if larger than cpl
  862.     var wrappedText=text.wordWrap(cpl,this.LINE_DELIMITER,false);
  863.     var textArray=this.createTextSet(wrappedText);
  864.     if(textArray.length==0){
  865.         return false;
  866.     }
  867.     var lines=this.createLines(textArray,cpl);
  868.     var lineCenter=Math.ceil(lines.length/2);
  869.     var shapeArray=null;
  870.     //ie has different algorithms for text line positioning for shapes and lines
  871.     if(this.isConnector(shape)){
  872.         shapeArray=this.handleConnectorTextLinePositioning(height,width,x,y,lineCenter,fontSize,lines,shapeFont,shape);
  873.         
  874.     }
  875.     else{
  876.         shapeArray=this.handleTextLinePositioning(height,width,x,y,lineCenter,fontSize,lines,shapeFont,shape);
  877.     }
  878.     //finally create a dummy shape to put all the text in
  879.     //debugger;
  880.     var dummyLine=new Object();
  881.     dummyLine.type=shape.id;
  882.     dummyLine.text="";
  883.     shapeFont.size=actualShapeFontSize;
  884.     shapeArray[shapeArray.length]=this.createTextShape(dummyLine,false,0,0,0,0,shapeFont,shape);
  885.     if(this.isConnector(shape)){
  886.         this.handleConnectorTextBackgroundPositioning(shape,rect,text);
  887.     }
  888.     //now add all the shapes to the shape
  889.     for(var i=0;i<shapeArray.length;i++){
  890.         shape.appendChild(shapeArray[i]);
  891.     
  892.     }
  893.     var textData=this.setTextData(shape,text);
  894. }
  895.  
  896. /**
  897.  * create the background rect element, this is the background shape for connector text
  898.  * @param the connector shape
  899.  */
  900. AbstractRenderer.prototype.getConnectorBackgroundShape=function(shape){
  901.     var bgShape=null;
  902.     if(shape.getElementsByTagName("rect").length>0)
  903.         bgShape=shape.getElementsByTagName("rect")[0];
  904.     else{
  905.         bgShape=this.createElement("rect",this.VML_SVG_NAMESPACE);
  906.         bgShape.style.position='absolute';
  907.         this.setFillColor(shape,"white");
  908.         this.setOpacity(shape,"1.0");
  909.         var subject=this.getShapeSubject(shape);
  910.         bgShape.style.zIndex=subject.style.zIndex;
  911.         //else
  912.         //bgShape.style.zIndex=this.maxIndex;
  913.         this.setStrokeWidth(bgShape,"0px");
  914.         
  915.     }
  916.     return bgShape;
  917. }
  918.  
  919. /**
  920.  * starting version 0.3.7, the text will be stored in a comment field inside the c:textData element, this is done 
  921.  * for optimization purposes and because newlines were being lost by placing the text in attributes, In 
  922.  * addition the text will be htmlEncoded to prevent invalid xml
  923.  */
  924. AbstractRenderer.prototype.setTextData=function(shape,text){
  925.     
  926.     if(!text)text="";
  927.     //first clean up existing nodes
  928.     var nodes=shape.getElementsByTagName("textData");
  929.     var nodeArray=$A(nodes);
  930.     for(var i=0;i<nodeArray.length;i++){
  931.         var node=nodeArray[i];
  932.         this.remove(node);
  933.     }
  934.         var textData=this.createElement("c:textData","CUMULATE_LABS");
  935.         var textNode=this.container.ownerDocument.createComment(JH.Utilities.HtmlEncode2(text));
  936.         textData.appendChild(textNode);
  937.         textData.setAttribute("version","0.3.7");
  938.         shape.appendChild(textData);
  939.     
  940.  
  941.  
  942. }
  943. /**
  944.  * starting version 0.3.6, the text will be stored in a comment field inside the c:textData element, this is done 
  945.  * for optimization purposes and because newlines were being lost by placing the text in attributes
  946.  * Update:since 0.3.7 we are placing encoded html text instead of just escaped one...
  947.  */
  948. AbstractRenderer.prototype.getShapeText=function (shape,otherDoc){
  949.     //this is pre 0.3.2, we will eventually remove this
  950.     var element=document.getElementById("text:dummy"+shape.id);
  951.     //for pre 0.3.6
  952.     if(element){
  953.         return this.getShapeTextDeprecated(shape,otherDoc);
  954.     }
  955.     var nodes=shape.getElementsByTagName("textData");
  956.     var version=null;
  957.     if(nodes.length>0)
  958.         version=nodes[0].getAttribute("version");
  959.     if(nodes.length==0||version==null||version==""){
  960.         return  this.getShapeTextDeprecated(shape,otherDoc);
  961.     }
  962.     else {
  963.         var comment=nodes[0].firstChild;
  964.         if(comment==null)return "";
  965.         var text=comment.nodeValue;
  966.         if(version=="0.3.6"){
  967.             text=unescape(text);
  968.         }
  969.         else{//since 0.3.7 we are placing encoded html text instead of just escaped one...
  970.             text=JH.Utilities.HtmlDecode2(text);    
  971.         }
  972.         return text;
  973.     }
  974.     
  975. }
  976. /**
  977. *this method will return the actual text of the shape which has been hidden in a dummy shape
  978. */
  979. AbstractRenderer.prototype.getShapeTextDeprecated=function(shape,otherDoc){
  980.     
  981.     if(!shape){
  982.         return null;
  983.     }
  984. //we keep this for backward compatibility...we are not going to use this
  985. //starting 0.3, we use c:textData elements to store data
  986.     var element=document.getElementById("text:dummy"+shape.id);
  987.     if(element) return element.string;
  988. //this is how post 0.2 implementations will handle text data 
  989.     else{
  990.         var string="";
  991.         var nodes=shape.getElementsByTagName("textData");
  992.         for(var i=0;i<nodes.length;i++){
  993.             string+=nodes[i].getAttribute("string");
  994.             
  995.         }
  996.         return string;
  997.     }
  998.  
  999. }
  1000.  
  1001. /**
  1002. *create the lines
  1003. *@param array of lines
  1004. *@param characters for line
  1005. */
  1006. AbstractRenderer.prototype.createLines=function(textArray,charsPerLine){
  1007.     var lineArray=new Array();
  1008.     for(var i=0;i<textArray.length;i++){
  1009.         var stringVal=textArray[i];
  1010.         if(stringVal.length<charsPerLine){
  1011.             var lineObj=new Object();
  1012.             lineObj.text=stringVal;
  1013.             lineObj.type="normal";
  1014.             lineArray[lineArray.length]=lineObj;
  1015.         }
  1016.         else{
  1017.             var numlines=Math.floor((stringVal.length/charsPerLine));
  1018.             var remainder=stringVal.length%charsPerLine;
  1019.             var j=0;
  1020.             for(j=0;j<numlines;j++){
  1021.                 var line=stringVal.substr((j*charsPerLine),charsPerLine);
  1022.                 var lineObj=new Object();
  1023.                 lineObj.text=line;
  1024.                 if(j==0){
  1025.                     lineObj.type="newline";
  1026.                 }
  1027.                 else{
  1028.                     lineObj.type="normal";
  1029.                 }
  1030.                 lineArray[lineArray.length]=lineObj;
  1031.             }
  1032.             if(remainder>0){
  1033.                 var line=stringVal.substr((j*charsPerLine),remainder);
  1034.                 var lineObj=new Object();
  1035.                 lineObj.text=line;
  1036.                 if(numlines==0){
  1037.                     lineObj.type="newline";
  1038.                 }
  1039.                 else{
  1040.                     lineObj.type="normal";
  1041.                 }
  1042.                 lineArray[lineArray.length]=lineObj;
  1043.             }
  1044.         }
  1045.     
  1046.     }
  1047.     return lineArray;
  1048.  
  1049. }
  1050.  
  1051. /**
  1052. * for shapes, return the text bounds if it exists, otherwise return the dimensions of the shape. For connectors
  1053. * return the text width and height (or reasonable default) anchored around the center point
  1054. * @param shape
  1055. * @param connectorText (optional)--only passed for connectors
  1056. * @param current zoom factor
  1057. */
  1058. AbstractRenderer.prototype.getTextBounds=function(shape, connectorText,zoomFactor,font){
  1059.     if(this.isConnector(shape)){
  1060.         var center=this.getConnectorCenterPoint(shape);
  1061.         var textSize=this.getConnectorTextSize(shape,connectorText,zoomFactor,font);
  1062.         var bounds=new Object();
  1063.         bounds.width=textSize.width;
  1064.         bounds.height=textSize.height;
  1065.         bounds.x=(center.x-bounds.width/2);
  1066.         bounds.y=(center.y-bounds.height/2);
  1067.         return bounds;
  1068.     }
  1069.     else{
  1070.         var rect=this.bounds(shape);
  1071.         var textBounds=shape.getElementsByTagName("text-bound");
  1072.         if(textBounds.length==0)return rect;
  1073.         else{
  1074.             var bounds=rect;
  1075.             var textb=textBounds[0];
  1076.             var x=this.getAttribute(textb,"fromX");
  1077.             var y=this.getAttribute(textb,"fromY");
  1078.             var x2=this.getAttribute(textb,"toX");
  1079.             var y2=this.getAttribute(textb,"toY");
  1080.             var newX=(x*bounds.width/1000)+(bounds.x*1);
  1081.             var newY=(y*bounds.height/1000)+(bounds.y*1);
  1082.             var width=(x2-x)*(bounds.width/1000);
  1083.             var height=(y2-y)*(bounds.height/1000)
  1084.             bounds.x=bounds.left=newX;
  1085.             bounds.y=bounds.top=newY;
  1086.             var textSize=this.getConnectorTextSize(shape,connectorText,zoomFactor,font);
  1087.             bounds.width=textSize.width;
  1088.             bounds.height=textSize.height;
  1089.             return bounds;
  1090.         }
  1091.     }    
  1092. }
  1093. /**
  1094. *break the text up by new lines \n, called from setShapeText
  1095. */
  1096. AbstractRenderer.prototype.createTextSet=function(text){
  1097.     var textArray=new Array();
  1098.     if(!text||text.length==0){
  1099.         return textArray;
  1100.     }
  1101.     else{
  1102.         textArray=text.split(this.LINE_DELIMITER);    
  1103.         return textArray;
  1104.     }
  1105. }
  1106.  
  1107. /**
  1108. *Intialize an empty font object with the remaining default attributes (besides the one already populated)
  1109. * this way we don't have to poulate defaults everywhere a font is used.
  1110. * @param a partially filled font object
  1111. **/
  1112.  
  1113. AbstractRenderer.prototype.fillUpFont=function(font){
  1114.         if(font.size=='')
  1115.             font.size=16;
  1116.         if(font.family=='')
  1117.             font.family="'arial'";
  1118.         if(font.italics=='')
  1119.             font.italics="normal";
  1120.         if(font.bold=='')
  1121.             font.bold="normal"
  1122.         if(font.align=='')
  1123.             font.align="center";
  1124.         if(font.color=='')
  1125.             font.color="black";
  1126.             
  1127.  
  1128. }
  1129.